/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-04
 * V4.0
 */
package com.jphenix.one.h2;
//#region 【引用区】
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import com.jphenix.kernel.baseobject.instanceb.ABase;
import com.jphenix.kernel.classloader.ResourcesLoader;
import com.jphenix.kernel.objectloader.util.JarResourceVO;
import com.jphenix.share.lang.SBoolean;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.standard.docs.BeanInfo;
import com.jphenix.standard.docs.ClassInfo;
//#endregion

//#region 【说明区】
/**
 * 数据库Tcp服务管理类
 * 
 * 2019-07-15 支持自定义数据库根路径
 * 2021-03-04 增加了新的h2数据库自动创建库的参数
 * 2022-02-08 不能采用Class.forName方式构建类，因为jdbc包可能不在/WEB-INF/lib 中，而是在其子文件夹中
 * 2024-08-08 新增如果外部目标路径没有数据库文件，则从jar包中复制出来
 * 2024-08-17 适配了嵌入Spring架构
 * 
 * com.jphenix.one.h2.DbTcpServer
 * 
 * @author 马宝刚
 * 2009-7-28 下午02:07:55
 */
//#endregion
@ClassInfo({"2024-08-17 23:20","数据库Tcp服务管理类"})
@BeanInfo({"dbtcpserver"})
public class DbTcpServer extends ABase {

	//#region 【声明区】
	// 服务器类路径
	private final String SERVER_CLASSPATH = "org.h2.tools.Server";
	
	protected String dbPort = null; //数据库访问端口号
	protected String dbBasePath = null; //数据库文件根路径
	protected Object dbTcpServer = null; //数据库服务
	protected boolean tcpAllowOther = false; //是否其它电脑访问数据库端口
	protected boolean enabledLocalDbServer = false; //是否启用本地数据库服务
	//#endregion
	
	//#region start() 启动数据库服务
	/**
	 * 启动数据库服务
	 * 马宝刚
	 * 2009-7-28 下午02:38:09
	 * @throws Exception 执行发生异常
	 */
	public void start() throws Exception {
		if (!isEnabledLocalDbServer()) {
			return;
		}
		checkDeployDbFile(); // 检测项目中是否存在数据库文件夹，如果不存在，则从jar包中解包出数据库文件
		//构建配置参数
		ArrayList<String> argList = new ArrayList<String>();
		argList.add("-tcpPort");
		argList.add(getDbPort());
		argList.add("-baseDir");
		argList.add(getDbBasePath());
		if (isTcpAllowOther()) {
			argList.add("-tcpAllowOthers");
		}
	    //新版本的h2数据库默认不在支持自动创建库，加上这个参数就老实了
	    argList.add("-ifNotExists");
		//构建服务类
		//Class<?> serverCls = Class.forName(SERVER_CLASSPATH); // 不能采用这种方式构建类，因为jdbc包可能不在/WEB-INF/lib 中，而是在其子文件夹中
	    Class<?> serverCls = getBeanFactory().getClassLoader().loadClass(SERVER_CLASSPATH);
	    
		//构建服务方法
		Method cts = 
			serverCls.getDeclaredMethod(
					"createTcpServer", String[].class);
		//构建数据库TCP服务
		dbTcpServer = cts.invoke(null, BaseUtil.listToArray(argList,String.class));
		//构建启动方法
		Method startMh = 
			dbTcpServer.getClass().getMethod("start");
		log.startLog("Start DB Server: Port:["+getDbPort()+"] BasePath:["
				+getDbBasePath()+"] TcpAllowOther:["
				+(isTcpAllowOther()?"true":"false")+"]");
		//启动服务
		startMh.invoke(dbTcpServer);
	}
	//#endregion
	
	//#region setEnabledLocalDbServer(enabledLocalDbServerStr) 设置是否启用本地数据库服务
	/**
	 * 设置是否启用本地数据库服务
	 * 马宝刚
	 * 2009-7-28 下午02:43:42
	 * @param enabledLocalDbServerStr 是否启用本地数据库服务布尔字符串 yes no
	 */
	public void setEnabledLocalDbServer(String enabledLocalDbServerStr) {
		enabledLocalDbServer = 
			SBoolean.valueOf(enabledLocalDbServerStr);
	}
	//#endregion
	
	//#region setEnabledLocalDbServer(enabledLocalDbServer) 设置是否启用本地数据库服务
	/**
	 * 设置是否启用本地数据库服务
	 * 马宝刚
	 * 2009-7-28 下午02:43:42
	 * @param enabledLocalDbServer 是否启用本地数据库服务
	 */
	public void setEnabledLocalDbServer(boolean enabledLocalDbServer) {
		this.enabledLocalDbServer = enabledLocalDbServer;
	}
	//#endregion
	
	//#region isEnabledLocalDbServer() 是否启用本地数据库服务
	/**
	 * 是否启用本地数据库服务
	 * 马宝刚
	 * 2009-7-28 下午02:45:03
	 * @return 是否启用本地数据库服务
	 */
	public boolean isEnabledLocalDbServer() {
		return enabledLocalDbServer;
	}
	//#endregion
	
	//#region stop() 停止数据库服务
	/**
	 * 停止数据库服务
	 * 马宝刚
	 * 2009-7-28 下午02:38:31
	 */
	public void stop(){
		if (dbTcpServer==null) {
			return;
		}
		try {
			//构建终止方法
			Method shutdownMh = 
				dbTcpServer.getClass().getMethod("stop");
			//执行终止
			shutdownMh.invoke(dbTcpServer);
		}catch(Exception e) {}
	}
	//#endregion
	
	//#region setDbPort(dbPort) 设置数据库访问端口号
	/**
	 * 设置数据库访问端口号
	 * 马宝刚
	 * 2009-7-28 下午02:33:50
	 * @param dbPort 数据库访问端口号
	 */
	public void setDbPort(String dbPort) {
		this.dbPort = dbPort;
	}
	//#endregion
	
	//#region getDbPort() 获取数据库访问端口号
	/**
	 * 获取数据库访问端口号
	 * 马宝刚
	 * 2009-7-28 下午02:34:03
	 * @return 数据库访问端口号
	 */
	public String getDbPort() {
		if(dbPort==null) {
			dbPort = "8348";
		}
		return dbPort;
	}
	//#endregion
	
	//#region setDbBasePath(dbBasePath) 获取数据库文件根路径
	/**
	 * 获取数据库文件根路径
	 * 马宝刚
	 * 2009-7-28 下午02:35:45
	 * @param dbBasePath 数据库文件根路径
	 */
	public void setDbBasePath(String dbBasePath) {
		this.dbBasePath = infPath(p("file_sources")+dbBasePath);
	}
	//#endregion
	
	//#region getDbBasePath() 设置数据库文件根路径
	/**
	 * 设置数据库文件根路径
	 * 马宝刚
	 * 2009-7-28 下午02:35:56
	 * @return 数据库文件根路径
	 */
	public String getDbBasePath() {
		if (dbBasePath==null) {
			dbBasePath = infPath(p("file_sources")+"/db");
		}
		return dbBasePath;
	}
	//#endregion

	//#region isTcpAllowOther() 是否其它电脑访问数据库端口
	/**
	 * 是否其它电脑访问数据库端口
	 * 马宝刚
	 * 2009-7-28 下午04:09:16
	 * @return 是否其它电脑访问数据库端口
	 */
	public boolean isTcpAllowOther() {
		return tcpAllowOther;
	}
	//#endregion
	
	//#region setTcpAllowOther(tcpAllowOther) 设置是否其它电脑访问数据库端口
	/**
	 * 设置是否其它电脑访问数据库端口
	 * 马宝刚
	 * 2009-7-28 下午04:09:25
	 * @param tcpAllowOther 是否其它电脑访问数据库端口
	 */
	public void setTcpAllowOther(boolean tcpAllowOther) {
		this.tcpAllowOther = tcpAllowOther;
	}
	//#endregion
	
	//#region setTcpAllowOther(tcpAllowOtherStr) 设置是否其它电脑访问数据库端口
	/**
	 * 设置是否其它电脑访问数据库端口
	 * 马宝刚
	 * 2009-7-28 下午04:09:25
	 * @param tcpAllowOtherStr 是否其它电脑访问数据库端口
	 */
	public void setTcpAllowOther(String tcpAllowOtherStr) {
		tcpAllowOther = SBoolean.valueOf(tcpAllowOtherStr);
	}
	//#endregion
	
	//#region 【内部方法】checkDeployDbFile() 检测项目中是否存在数据库文件夹，如果不存在，则从jar包中解包出数据库文件
	/**
	 * 检测项目中是否存在数据库文件夹，如果不存在，则从jar包中解包出数据库文件
	 * 2024年8月8日
	 * @author MBG
	 */
	private void checkDeployDbFile() {
		// 数据库根路径
		String dbPath = sourcePath("/h2db");
		// 路径对象
		File pathObj = new File(dbPath);
		
		if(pathObj.exists()) {
			return;
		}
		
		// 资源加载器
		ResourcesLoader rl = bean(ResourcesLoader.class);
		
		info("The Database Base Path:["+dbPath+"] Not Exist,Begin Extract From SDK\n");
		// 在jar包中的相对路径
		String jarSubPath = "resources/h2db";
		
		// 获取数据库文件资源序列 key中并没有 /resources 路径，但资源元素是带的
		List<JarResourceVO> jrVOs = rl.getChildSources("h2db/");
        File             outFile;                   // 输出文件对象
        File             pathFile;                  // 输出文件路径对象
        FileOutputStream fos      = null;           // 文件写入流
		InputStream      is       = null;           // 压缩包读取流
		byte[]            buffer  = new byte[2048]; // 读取缓存
		int bytesRead;                              // 读取字节数
		for(JarResourceVO vo:jrVOs) {
			outFile = new File(dbPath+vo.entrie.getName().substring(jarSubPath.length()));
			
        	//#region 检测目标文件夹是否存在
        	pathFile = outFile.getParentFile();
        	if(!pathFile.exists()) {
        		pathFile.mkdirs();
        	}
        	//#endregion
        	
        	try {
        		fos = new FileOutputStream(outFile);
    			is = vo.jarFile.getInputStream(vo.entrie);
    			while ((bytesRead = is.read(buffer)) != -1) {
    				fos.write(buffer, 0, bytesRead);
    			}
    			fos.flush();
        	}catch(Exception e) {
        		e.printStackTrace();
        	}finally {
        		try {
        			is.close();
        		}catch(Exception e2) {}
        		try {
        			fos.close();
        		}catch(Exception e2) {}
        	}
		}
	}
	//#endregion
}









